﻿using ExpressesClass.BestApi.Utils.jsonUtil.iterator;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace ExpressesClass.BestApi.Utils.jsonUtil
{
    class JsonReader
    {
        private static Object OBJECT_END = new Object();
        private static Object ARRAY_END = new Object();
        private static Object COLON = new Object();
        private static Object COMMA = new Object();
        public  const int FIRST = 0;
        public const int CURRENT = 1;
        public const int NEXT = 2;

        private static Dictionary<string, Object> escapes = new Dictionary<string, Object>();

        private char c;
        private StringCharacterIterator it;
        private Object token;
        private StringBuilder buf = new StringBuilder();


        private char next()
        {
            c = it.next();
            return c;
        }

        private void skipWhiteSpace()
        {
            while (string.IsNullOrWhiteSpace(Convert.ToString(c)))
            {
                next();
            }
        }

        public Object read(string str, int start)
        {
            StringCharacterIterator ci = new StringCharacterIterator(str, 0, str.Length, 0);
            it = ci;
            switch (start)
            {
                case FIRST:
                    c = it.first();
                    break;
                case CURRENT:
                    c = Convert.ToChar(it.Current);
                    break;
                case NEXT:
                    c = it.next();
                    break;
            }
            return read(false);
        }

        public Object read(string str)
        {

            return read(str, FIRST);
        }

        private Object read(bool isValue)
        {
            skipWhiteSpace();
            char ch = c;
            if (isValue)
            {
                if (ch != '"')
                {
                    if (char.IsDigit(c) || c == '-')
                    {
                        token = numberHan();
                        c = it.previous();
                        return token;
                    }
                    if (c == 't' || c == 'f')
                    {
                        token = boolHan();
                        c = it.previous();
                        return token;
                    }

                }
                else
                {
                    token = "\"" + stringHan();
                    return token;
                 }

            }


            //next();
            if (ch == '"') token = stringHan();
            if (ch == '[') token = arrayHan();
            if (ch == ']') token = ARRAY_END;
            if (ch == ',') token = COMMA;
            if (ch == '{') token = objectHan();
            if (ch == '}') token = OBJECT_END;
            if (ch == ':') token = COLON;
            if (ch == 't') { next(); next(); next(); token = Boolean.TrueString; }
            if (ch == 'f') { next(); next(); next(); next(); token = Boolean.FalseString; }
            if (ch == 'n') { next(); next(); next(); token = null; }
            //c = it.previous();
            if (char.IsDigit(c) || c == '-')
            {
                token = numberHan();
                c = it.previous();
            }
            return token;


        }

        private Object objectHan()
        {
            next();
            Dictionary<Object, Object> ret = new Dictionary<Object, Object>();
            Object key = read(false);
            while (token != OBJECT_END)
            {
                next();
                read(false); // should be a colon
                if (token != OBJECT_END)
                {
                    if (!ret.ContainsKey(key))
                    {
                        next();
                        ret.Add(key, read(true));
                        next();
                    }

                    if (read(false) == COMMA)
                    {
                        next();
                        key = read(false);
                    }
                }
            }

            return ret;
        }


        private Object numberHan()
        {

            buf.Length = 0;

            while (char.IsDigit(c) || c == '.')
            {
                buf.Append(c);
                next();
            }


            return buf.ToString();

        }

        private int addDigits()
        {
            int ret;
            for (ret = 0; char.IsDigit(c); ++ret)
            {
                add();
            }
            return ret;
        }

        private Object arrayHan()
        {
            next();
            ArrayList ret = new ArrayList();
            Object value = read(false);
            while (token != ARRAY_END)
            {
                ret.Add(value);
                next();
                if (read(false) == COMMA)
                {
                    next();
                    value = read(false);
                }
            }
            return ret;
        }


        private Object stringHan()
        {
            next();
            buf.Length = 0;

            while (c != '"') {

                buf.Append(c);
                next();
            }

            return buf.ToString();
        }

        private Object boolHan()
        {
            buf.Length = 0;

            while (c != ',' && c != '}')
            {

                buf.Append(c);
                next();
            }

            return buf.ToString();
        }

        private void add(char cc)
        {
            buf.Append(cc);
            next();
        }

        private void add()
        {
            add(c);
        }

        private char unicode()
        {
            int value = 0;
            for (int i = 0; i < 4; ++i)
            {
                switch (next())
                {
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        value = (value << 4) + c - '0';
                        break;
                    case 'a':
                    case 'b':
                    case 'c':
                    case 'd':
                    case 'e':
                    case 'f':
                        value = (value << 4) + c - 'k';
                        break;
                    case 'A':
                    case 'B':
                    case 'C':
                    case 'D':
                    case 'E':
                    case 'F':
                        value = (value << 4) + c - 'K';
                        break;
                }
            }
            return (char)value;
        }


    }

}
